package org.setamv.springcloud.hystrix.comprehesive;

import com.netflix.discovery.util.StringUtil;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixCollapser;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixProperty;
import com.netflix.hystrix.contrib.javanica.cache.annotation.CacheResult;
import org.setamv.springcloud.entity.User;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.ParameterizedTypeReference;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpMethod;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.Future;

@Service
public class UserService {

    private static final Logger LOGGER = LoggerFactory.getLogger(UserService.class);

    public static final String SERVICE_NAME = "service";

    @Autowired
    private RestTemplate restTemplate;

    /**
     * 获取指定的用户信息，该方法指定了{@link HystrixCollapser}注解，所以多个请求会被收集到一起，由 {@link #getUsers(List)} 一起批量获取
     */
    @HystrixCollapser(batchMethod = "getUsers", collapserProperties = {
            @HystrixProperty(name = "timerDelayInMilliseconds", value = "1000")
    })
    public Future<User> getUser(String id) {
        LOGGER.info("getUser, id = {}", id);
        return null;
    }

    /**
     * 批量获取用户信息.<p>
     * 下面Hystrix应该有一个bug，就是，当该方法作为多个{@link #getUser(String)}方法的Collapser时，该方法去获取cacheKey的时候，传入的是方法{@link #getUser(String)}的参数，
     * 将导致参数类型不匹配。
     * @param ids
     * @return
     */
//    @CacheResult(cacheKeyMethod = "getUsersCacheKey")
    @HystrixCommand(groupKey = "userGroup", commandKey = "getUsers")
    public List<User> getUsers(List<String> ids) {
        LOGGER.info("getUser, ids = {}", ids);
        String joinedIds = StringUtil.join(ids.toArray(new String[0]));
        String url = "http://" + SERVICE_NAME + "/user/batch/" + joinedIds;

        HttpEntity<String> entity = new HttpEntity<>(ids.toString());
        ParameterizedTypeReference resultType = new ParameterizedTypeReference<List<User>>() {};
        ResponseEntity<List<User>> users = restTemplate.exchange(url, HttpMethod.GET, entity, resultType);
        return users.getBody();
    }

    public String getUserCacheKey(String id) {
        return "users[" + id + "]";
    }

    public String getUsersCacheKey(List<String> ids) {
        List<String> dupIds = new ArrayList<>(ids);
        Collections.sort(dupIds);
        String joinedIds = StringUtil.join(dupIds.toArray(new String[0]));
        return "users[" + joinedIds + "]";
    }

}
